const _ = require('lodash')

/*-------------------------------------------------------------------------------------------------------- */
//  非函数
const num1 = 2;
const num2 = 3;
const sum = num1 + num2;
// console.log(sum)

//  函数式(函数表达式，将函数作为一个变量赋值)
const add = (num1, num2) => {
    return num1 + num2;
}
// console.log(add(num1, num2))

/*-------------------------------------------------------------------------------------------------------- */
//  高阶函数(函数作为参数，函数作为返回值)
const forEach = (array, fun) => {
    for (let index = 0; index < array.length; index++) {
        const element = array[index];
        fun(element)
    }
}
//  forEach([1,2,3,4,5], num => console.log(num * 2))

const filter = (array, fun) => {
    const result = [];
    for (let index = 0; index < array.length; index++) {
        const element = array[index];
        if (fun(element)) result.push(element)
    }
    return result; 
}
// console.log(filter([1,2,3,4,5], num => num % 2 === 0))

const makeFn = () => {
    const msg = 'hello function';
    return _ => console.log(msg);
}

// makeFn()();
// fn();

const once = fn => {
    let done = false;
    return function() {
        if (!done) {
            done = true;
            fn.apply(this, arguments)
        }
    }
}

const pay = once((money) => {
    console.log(`支付${money}元钱`)
})
// pay(5)
// pay(5)
// pay(5)

const map = (array, fn) => {
    const result = [];
    for (const item of array) {
        result.push(fn(item))
    }
    return  result;
}
// console.log(map([1,2,3,4,5], num => num * 2))

const every = (array , fn) => {
    let result = true;
    for (const item of array) {
        result = fn(item);
        if (!result) break; 
    }
    return result;
}

// console.log(every([1,2,3,4,5], num => num % 2 === 0))
// console.log(every([2,4,6], num => num % 2 === 0))

const some = (array, fn) => {
    let result = false;
    for (const item of array) {
        result = fn(item)
        if (result) break;
    }
    return result;
}

// console.log(some([1,3,5], num => num % 2 === 0))
// console.log(some([2,4,6], num => num % 2 === 0))

/*-------------------------------------------------------------------------------------------------------- */

//  闭包

//  上面的makeFn、once都用到了闭包，闭包必然是一个高阶函数，因为把函数作为了返回值

const makePower = (power) => num => Math.pow(num, power);

// const power2 = makePower(2);
// const power3 = makePower(3);

// console.log(power2(3))
// console.log(power3(3))

/*-------------------------------------------------------------------------------------------------------- */

//  纯函数

const getArea = r => {
    console.log('我执行了！')
    return Math.PI * r * r;
};

const getAreaWithMemory = _.memoize(getArea);

//  记住了结果只会执行一次，当函数特别耗性能就可以使用
// getAreaWithMemory(2);
// getAreaWithMemory(2);
// getAreaWithMemory(2);

//  不纯函数,mini是一个外部的配置属性，如果改变了，则不满足纯函数的条件
const mini = 18;
const checkAge = age => age > mini;

//  修改成纯函数
const checkAge1 = age => {
    const mini = 18;
    return age > age;
}

/*-------------------------------------------------------------------------------------------------------- */

//  柯里化

const checkAge2 = mini => age => age > mini;

// const check18 = checkAge2(18);
// console.log(check18(20));

const getSum = (a, b, c) => a + b + c;

const curried = _.curry(getSum);

// console.log(curried(1, 2, 3));
// console.log(curried(1)(2)(3));
// console.log(curried(1, 2)(3));

const curriedFilter = _.curry((fn, array) => array.filter(fn))

// console.log(curriedFilter(num => num % 2 === 0)([1, 2, 3, 4]))

const curry = (fn) => {
    return function curriedFn(...args) {
        // 判断实参跟形参的个数
        if(args.length < fn.length){
            return function() {
                return curriedFn(...args, ...arguments);
            }
        }
        return fn(...args);
    }
}

const ownerCurriedFilter = curry((fn, array) => array.filter(fn))

// console.log(ownerCurriedFilter(num => num % 2 === 0, [1, 2, 3, 4]))
// console.log(ownerCurriedFilter(num => num % 2 === 0)([1, 2, 3, 4]))

/*-------------------------------------------------------------------------------------------------------- */

//  组合函数

const reverse = array => array.reverse();
const first = array => array[0];
const toUpper = str => str.toUpperCase();

const flowRightFn = _.flowRight(toUpper, first, reverse);

// console.log(flowRightFn(['aaa', 'bbb', 'ccc']));

const compose = (...args) => {
    return function(value) {
        return args.reverse().reduce((current, fn) => {
            return fn(current)
        }, value)
    }
}

const flowRightFn1 = compose(toUpper, first, reverse);

console.log(flowRightFn1(['aaa', 'bbb', 'ccc']));

/*-------------------------------------------------------------------------------------------------------- */

//  一个最基本的函子，一个容器，包裹一个值
class Container {
    //  of静态方法，可以省略new关键字创建对象
    static of (value) {
        return new Container(value)
    }

    constructor(value) {
        this._value = value
    }
    //  map方法，传入函数，将容器里的每一个值映射到另一个容器
    map(fn) {
        return Container.of(fn(this._value));
    }
}

Container.of(2)
.map(x => x + 2)
.map(x => x * x)

/*-------------------------------------------------------------------------------------------------------- */

IO函子

class IO {
    static of() {
        return new IO(function() {
            return x
        })
    }
    constructor(fn) {
        this._value = fn;
    }

    map(fn) {
        //  把当前的value和传入的fn组合成一个新的函数
        return new IO(fn.flowRight(fn, this._value))
    }
}
/*-------------------------------------------------------------------------------------------------------- */


